<?php

namespace src\config;
use FaaPz\PDO\Database;
use PDO;
use PDOStatement;
use PDOException;

class Db
{
    /**
     * @var Database 数据库连接实例
     */
    private $conn;
    /**
     * @var array 数据库配置
     */
    private $dbconfig;

    /**
     * @var string 错误信息
     */
    private $error;
    /**
     * @var PDOStatement 语句对象
     */
    private $stmt;

    public function __construct(array $dbconfig)
    {
        $this->dbconfig = $dbconfig;
        $this->connect();
    }
    /**
     * 连接数据库
     * 
     * @return bool 连接是否成功
     */
    private function connect()
    {
        try {
            $dsn = "{$this->dbconfig['driver']}:host={$this->dbconfig['host']};dbname={$this->dbconfig['database_name']};charset={$this->dbconfig['charset']}";
            $this->conn = new Database(
                $dsn,
                $this->dbconfig['username'],
                $this->dbconfig['password'],
                [
                    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                    PDO::ATTR_EMULATE_PREPARES => false // 禁用模拟预处理语句
                ]
            );
            return true;
        } catch (PDOException $e) {
            $this->error = $e->getMessage();
            return false;
        }
    }

    public function getConnection()
    {
        return $this->conn;
    }
    public function query(string $sql)
    {
        try {
            error_log("SQL: $sql");
            $this->stmt = $this->conn->query($sql);
            return $this->stmt;
        } catch (PDOException $e) {
            $this->error = $e->getMessage();
            return false;
        }
    }
    /**
     * 执行原生SQL语句
     * 
     * @param string $sql SQL语句
     * @param array $params 参数数组，支持索引数组(位置占位符)或关联数组(符号占位符)
     * @return bool 执行是否成功
     */
    public function execute(string $sql, array $params = []): bool
    {
        try {
            error_log("SQL: $sql");
            error_log("params:  " . implode(",", $params));

            $this->stmt = $this->conn->prepare($sql);

            // 绑定参数
            if (!empty($params)) {
                $this->bindParams($params);
            }

            // return $this->stmt->execute();
            $data = $this->stmt->execute();
            error_log("stmt->errorCode(): " . $this->stmt->errorCode());
            return $data;
        } catch (PDOException $e) {
            $this->error = $e->getMessage();
            // 从异常对象中获取 SQLSTATE 错误码
            error_log("PDOException SQLSTATE: " . $e->getCode());
            // 如果 stmt 已经创建，也可以直接取
            if ($this->stmt) {
                error_log("stmt->errorCode(): " . $this->stmt->errorCode());
                error_log("stmt->errorInfo(): " . implode(",", $this->stmt->errorInfo()));
            }
            throw new PDOException($e->getMessage(), (int) $e->getCode());
            // return false;
        }
    }
    /**
     * 绑定参数
     * 
     * @param array $params 参数数组
     */
    private function bindParams(array $params)
    {
        foreach ($params as $key => $value) {
            // 确定参数类型
            $paramType = PDO::PARAM_STR;
            if (is_int($value)) {
                $paramType = PDO::PARAM_INT;
            } elseif (is_bool($value)) {
                $paramType = PDO::PARAM_BOOL;
            } elseif (is_null($value)) {
                $paramType = PDO::PARAM_NULL;
            }

            // 绑定参数，支持位置占位符(数字索引)和符号占位符(字符串键名)
            $this->stmt->bindValue(
                is_string($key) ? $key : ($key + 1), // 位置占位符从1开始
                $value,
                $paramType
            );
        }
    }
    /**
     * 获取单条记录
     * 
     * @param string $sql SQL语句
     * @param array $params 参数数组
     * @return array|false 结果数组或false
     */
    public function fetch(string $sql, array $params = [])
    {
        // error_log("SQL: $sql");
        if ($this->execute($sql, $params)) {
            $data = $this->stmt->fetch();
            return $data;
        }
        return false;
    }
    /**
     * 获取多条记录
     * 
     * @param string $sql SQL语句
     * @param array $params 参数数组
     * @return array|false 结果数组或false
     */
    public function fetchAll(string $sql, array $params = [])
    {
        // error_log("SQL: $sql");
        if ($this->execute($sql, $params)) {
            $data = $this->stmt->fetchAll();
            return $data;
        }
        return false;
    }  /**
       * 获取记录数
       * 
       * @return int 记录数
       */
    public function rowCount(): int
    {
        return $this->stmt ? $this->stmt->rowCount() : 0;
    }  /**
       * 获取最后插入的ID
       * 
       * @return string|false 最后插入的ID
       */
    public function lastInsertId()
    {
        return $this->conn ? $this->conn->lastInsertId() : false;
    }
    /**
     * 开启事务
     * 
     * @return bool 是否成功
     */
    public function beginTransaction(): bool
    {
        return $this->conn ? $this->conn->beginTransaction() : false;
    }

    /**
     * 提交事务
     * 
     * @return bool 是否成功
     */
    public function commit(): bool
    {
        return $this->conn ? $this->conn->commit() : false;
    }

    /**
     * 回滚事务
     * 
     * @return bool 是否成功
     */
    public function rollBack(): bool
    {
        return $this->conn ? $this->conn->rollBack() : false;
    }

    /**
     * 获取错误信息
     * 
     * @return string 错误信息
     */
    public function getError(): string
    {
        return $this->error;
    }

    /**
     * 关闭数据库连接
     */
    public function close()
    {
        $this->conn = null;
        $this->stmt = null;
    }

}